package org.wattdepot.simulation.dorm;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import org.wattdepot.simulation.core.Simulation;

/**
 * Defines a Data object that returns data based on day and time.
 * 
 * @author BaoHuy Ung, Alan Lee, Edwin Lim, and Anthony Xu.
 * 
 */
public class Dorm extends Simulation {

  /** Random object. */
  private Random m_Random;
  /** Hash map for typical power usage on a weekday. */
  private Map<String, Double> weekdayHash = new HashMap<String, Double>();
  /** Hash map for typical power usage on a weekend. */
  private Map<String, Double> weekendHash = new HashMap<String, Double>();
  /**Temporary instance to hold data.*/
  private int temp = 0;
  /** Holds energy data. */
  private int energy = 0;
  /** Temporary instance to hold data(float).*/
  private float serverEnergy = 0;
  /** Holds accumulated energy data as int-bits. */
  private int energyBits = 0;
  /** Limit value. */
  private int limit = 0;
  /** Test value. */
  private int test = 0;
  /** Maximum value of power in a day. */
  private double maxWatts = 20000;
  /** Holds overflow value. */
  private int overflow = 0;
  /** Holds bit value. */
  private int bits = 0;
  /** Hold PT bit value. */
  private int ptBits = 0;
  /** Holds PT2 bit value. */
  private int pt2Bits = 0;
  /** Holds the sum. */
  private float energySum = 0;

  /**
   * Constructs a dorm object.
   */
  public Dorm() {
    // declare random object
    m_Random = new Random();
    // fill weekdayHash with data
    weekdayHash.put("000", .6);
    weekdayHash.put("030", .55);
    weekdayHash.put("100", .4);
    weekdayHash.put("130", .3);
    weekdayHash.put("200", .2);
    weekdayHash.put("230", .15);
    weekdayHash.put("300", .15);
    weekdayHash.put("330", .15);
    weekdayHash.put("400", .15);
    weekdayHash.put("430", .15);
    weekdayHash.put("500", .15);
    weekdayHash.put("530", .16);
    weekdayHash.put("600", .17);
    weekdayHash.put("630", .20);
    weekdayHash.put("700", .23);
    weekdayHash.put("730", .25);
    weekdayHash.put("800", .25);
    weekdayHash.put("830", .27);
    weekdayHash.put("900", .28);
    weekdayHash.put("930", .30);
    weekdayHash.put("1000", .33);
    weekdayHash.put("1030", .35);
    weekdayHash.put("1100", .37);
    weekdayHash.put("1130", .39);
    weekdayHash.put("1200", .38);
    weekdayHash.put("1230", .41);
    weekdayHash.put("1300", .43);
    weekdayHash.put("1330", .44);
    weekdayHash.put("1400", .46);
    weekdayHash.put("1430", .50);
    weekdayHash.put("1500", .51);
    weekdayHash.put("1530", .55);
    weekdayHash.put("1600", .57);
    weekdayHash.put("1630", .59);
    weekdayHash.put("1700", .63);
    weekdayHash.put("1730", .65);
    weekdayHash.put("1800", .68);
    weekdayHash.put("1830", .70);
    weekdayHash.put("1900", .72);
    weekdayHash.put("1930", .75);
    weekdayHash.put("2000", .77);
    weekdayHash.put("2030", .78);
    weekdayHash.put("2100", .80);
    weekdayHash.put("2130", .82);
    weekdayHash.put("2200", .80);
    weekdayHash.put("2230", .77);
    weekdayHash.put("2300", .76);
    weekdayHash.put("2330", .74);
    weekdayHash.put("2400", .70);
    // fill weekdayHash with data
    weekendHash.put("000", .6);
    weekendHash.put("030", .62);
    weekendHash.put("100", .6);
    weekendHash.put("130", .59);
    weekendHash.put("200", .58);
    weekendHash.put("230", .4);
    weekendHash.put("300", .3);
    weekendHash.put("330", .29);
    weekendHash.put("400", .27);
    weekendHash.put("430", .25);
    weekendHash.put("500", .25);
    weekendHash.put("530", .23);
    weekendHash.put("600", .21);
    weekendHash.put("630", .22);
    weekendHash.put("700", .23);
    weekendHash.put("730", .25);
    weekendHash.put("800", .25);
    weekendHash.put("830", .27);
    weekendHash.put("900", .26);
    weekendHash.put("930", .26);
    weekendHash.put("1000", .27);
    weekendHash.put("1030", .28);
    weekendHash.put("1100", .29);
    weekendHash.put("1130", .3);
    weekendHash.put("1200", .31);
    weekendHash.put("1230", .32);
    weekendHash.put("1300", .33);
    weekendHash.put("1330", .35);
    weekendHash.put("1400", .4);
    weekendHash.put("1430", .5);
    weekendHash.put("1500", .51);
    weekendHash.put("1530", .55);
    weekendHash.put("1600", .57);
    weekendHash.put("1630", .59);
    weekendHash.put("1700", .6);
    weekendHash.put("1730", .63);
    weekendHash.put("1800", .62);
    weekendHash.put("1830", .61);
    weekendHash.put("1900", .62);
    weekendHash.put("1930", .63);
    weekendHash.put("2000", .63);
    weekendHash.put("2030", .64);
    weekendHash.put("2100", .63);
    weekendHash.put("2130", .64);
    weekendHash.put("2200", .65);
    weekendHash.put("2230", .65);
    weekendHash.put("2300", .66);
    weekendHash.put("2330", .65);
    weekendHash.put("2400", .66);
  }

  /**
   * Checks to see if m_Random is set.
   * 
   * @return boolean if m_random is set.
   */
  public boolean isSet() {
    return m_Random.nextBoolean();
  }

  /**
   * Returns true.
   * 
   * @return true
   */
  public boolean isValid() {
    return true;
  }

  /**
   * Generates a random power value based on day and time of day.
   * 
   * @return power value
   */
  public float createRandomValue() {
    // get the current day, hour, and minute
    Calendar now = Calendar.getInstance();
    int day = now.get(Calendar.DAY_OF_WEEK);
    int hour = now.get(Calendar.HOUR_OF_DAY);
    int minute = now.get(Calendar.MINUTE);
    // if minute is less than 15, round down
    if (minute < 15) {
      minute = 00;
    }
    // if minute if greater than 45, round up
    else if (minute > 45) {
      hour++;
      minute = 00;
    }
    // if minute is between 15 and 45, round to 30
    else {
      minute = 30;
    }
    // convert hour to string
    String time = Integer.toString(hour);
    // concatenates a "00" if rounded to the nearest hour
    if (minute == 0) {
      time += "00";
    }
    // concatenates a "30"
    else {
      time += "30";
    }
    // retrieve power level percentage from hash table
    Double powerPercentage;
    // choose the right hash table of weekend or weekday
    if ((day == 1) || (day == 7)) {
      powerPercentage = weekendHash.get(time);
    }
    else {
      powerPercentage = weekdayHash.get(time);
    }
    // gets the percentage of the max power
    Double power = powerPercentage * maxWatts;
    // lower limit of random values (10% lower)
    Double min = power * .9;
    // upper limit of random values (10% greater)
    Double max = power * 1.1;
    // new Random data
    Random d_Random = new Random();
    // gets a random value between the min and max values
    float result = (float) ((d_Random.nextInt((int) (max - min)) + d_Random.nextFloat() + min));
    setIntBits(result);
    return result / 1000;
  }

  /**
   * Returns power in bits.
   * 
   * @return power in bits.
   */
  public int getIntBits() {
    return bits;
  }

  /**
   * Converts to int bits.
   * 
   * @param x Input float to be converted.
   */
  public void setIntBits(float x) {
    bits = Float.floatToIntBits(x);
  }
  
  /**
   * Accumulated energy value as float returned.
   * 
   * @return int bits of the float sum.
   */
  public int getEnergyBits() {
    return energyBits;
  }
  
  /**
   * Convert float to int-bits and stored until called.
   * 
   * @param x the sum of accumulated energy.
   */
  public void setEnergyBits(float x) {
    energyBits = Float.floatToIntBits(x);
  }

  /**
   * Returns energy value.
   * 
   * @return energy value.
   */
  public int getEnergy() {
    return energy;
  }

  /**
   * Sets the energy value.
   * 
   * @param a The energy value.
   */
  public void setEnergy(int a) {
    energy = a;
  }
  
  /**
   * Sets the latest energy data from WattDepot server.
   * 
   * @param a latest WattDepot server energy
   */
  public void setServerEnergy(int a) {
    temp = a;
  }
  
  /**
   * Sets the latest energy data from WattDepot server as float.
   * 
   * @param a latest WattDepot server energy.
   */
  public void setEnergyFloat(double a) {
    serverEnergy = (float)a; 
  }
  
  /**
   * Return latest energy data from WattDepot server.
   * 
   * @return WattDepot server energy data.
   */
  public float getEnergyFloat() {
    return serverEnergy;
  }
  
  /**
   * Returns latest energy data from WattDepot server.
   * 
   * @return WattDepot server energy data
   */
  public int getServerEnergy() {
    return temp;
  }

  /**
   * Returns the limit.
   * 
   * @return Limit value.
   */
  public int getLimit() {
    return limit;
  }

  /**
   * Sets the limit value.
   * 
   * @param a The limit value.
   */
  public void setLimit(int a) {
    limit = a;
  }

  /**
   * Computes the total energy used.
   * 
   * @param powerBitValue The randomly generated power level.
   * @return The energy value.
   */
  public float totalAccumulatedEnergy(int powerBitValue) {
    int power = powerBitValue;
    float level = ((Float.intBitsToFloat(power)) / 3600) / 1000;
    energySum += level;
    double serverData = (double) getServerEnergy() / 1000;
    setEnergyBits(energySum * 1000 + (float)serverData + getEnergyFloat());
    if (energySum >= 0.1) {
      float converter = energySum * 10;
      test = (int) converter + getServerEnergy();
      if (test >= 65535) {
        overflow = test / 65535;
        setLimit(overflow);
        test = test % 65535;
      }
      else {
        setLimit(0);
      }
    }
    else {
      test = getServerEnergy();
      if (test == 0) {
        test = 1;
      }
      else if (test >= 65535) {
        overflow = test / 65535;
        setLimit(overflow);
        test = test % 65535;
      }
    }
    setEnergy(test);
    return energySum;
  }

  /**
   * Generates a random value for the Potential Transformer #1.
   * 
   * @return Potential Transformer #1 data.
   */
  public float createRandomPT1Value() {
    float randomval = (m_Random.nextInt(500001) + 50);
    setBitsForPT(randomval);
    return randomval;
  }

  /**
   * Returns the Potential Transformer #1 value in bits.
   * 
   * @return Potential Transformer #1 value in bits.
   */
  public int getBitsForPT() {
    return ptBits;
  }

  /**
   * Sets the Potential Transformer #1 value in bits.
   * 
   * @param x Potential Transformer #1 value to be converted to bits.
   */
  public void setBitsForPT(float x) {
    ptBits = Float.floatToIntBits(x);
  }

  /**
   * Generates a random value for the Potential Transformer #2.
   * 
   * @return Potential Transformer #2 data.
   */
  public int createRandomPT2Value() {
    int randomval = (m_Random.nextInt(40001) + 50);
    setPT2(randomval);
    return randomval;
  }

  /**
   * Returns the Potential Transformer #2 value in bits.
   * 
   * @return Potential Transformer #2 value in bits.
   */
  public int getPT2() {
    return pt2Bits;
  }

  /**
   * Sets the Potential Transformer #2 value in bits.
   * 
   * @param x Potential Transformer #2 value in bits.
   */
  public void setPT2(int x) {
    pt2Bits = x;
  }

  /**
   * Generates a random value for the Current Transformer #1.
   * 
   * @return Current Transformer #1 data.
   */
  public int createRandomCT1Value() {
    int randomval = (m_Random.nextInt(50001));
    if (randomval == 0) {
      randomval = 1;
    }
    return randomval;
  }

  /**
   * Generates a random value for the Current Transformer #2.
   * 
   * @return Current Transformer #2 data.
   */
  public int createRandomCT2Value() {
    int randomval = (m_Random.nextInt(1000) + 1);
    if ((randomval % 5) == 0) {
      randomval = 5;
    }
    else {
      randomval = 1;
    }
    return randomval;
  }

}